#!/usr/bin/python
# Copyright (C) 2003, Charles Wang.
# Author:  Charles Wang <charles@linux.net.cn>
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANT; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public LIcense for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.

# Usage:
#    GenKModList.py [options] module......
#
# Option:
#    -o cutdown_pcitable     Generate a cutdown pcitable if specified.
#    -t pcitable             Use the specified pcitable if specified. The
#                            default value is /usr/share/hwdata/pcitable.
#    -q cutdown_pci.ids      Generate a cutdown pci.ids if specified.
#    -d pci.ids              Use the specified pci.ids if specified. The
#                            default value is /usr/share/hwdata/pci.ids
#    -r rootdir
#    -k kernelversion
#
# Module list is a list of considered list. Directory is permitted also.
# If a directory is present, then all the modules in the directory are
# included. If the module or directory name are prefixed with '@', so that
# modules and their dependencies will be reserved anyway. Otherwise, the
# module will be removed if it is not requested by pcitable.
#
# The output of this script is the kernel module list which remove the module
# which is not referenced by pcitable.

import os
import string
import sys

import kmdeps

from getopt import getopt

cutdown_f = None
pcitable = None #'/usr/share/hwdata/pcitable'
cutdown_pciids_f = None
pciids = None #'/usr/share/hwdata/pci.ids'
rootdir = '/'
kernelver = os.uname()[2]
optlist, args = getopt(sys.argv[1:], 'o:t:q:d:r:k:')

for (optname, optval) in optlist:
    if optname == '-o':
        if cutdown_f:  cutdown_f.close()
        cutdown_f = file(optval, 'w')
    elif optname == '-t':
        pcitable = os.path.abspath(optval)
    elif optname == '-q':
        if cutdown_pciids_f: cutdown_pciids_f.close()
        cutdown_pciids_f = file(optval, 'w')
    elif optname == '-d':
        pciids = os.path.abspath(optval)
    elif optname == '-r':
        rootdir = optval
    elif optname == '-k':
        kernelver = optval

os.chdir(rootdir)

# module file extension
mod_ext = kernelver[:3] > '2.4' and '.ko' or '.o'

# Load the module dependence map.
depfn = os.path.join('lib/modules', kernelver, 'modules.dep')
kmd = kmdeps.kmdict()
try:
    f = file(depfn)
    kmd.loaddeps(f, kernelver)
    f.close()
except:
    print "Load %s failed, the rootdir(%s) or kernel version(%s) might be wrong.\n" % (depfn, rootdir, kernelver)

kmod_map = {}

def scan_km_in_dir(must_reserve, dirname, name):
    for bn in name:
        filename = os.path.join(dirname, bn)
        if os.path.isfile(filename) and os.path.splitext(filename)[1] == mod_ext:
            kmobj = kmd['/' + filename]
            if not kmod_map.has_key(kmobj):
                kmod_map[kmobj] = must_reserve
# Get all requested modules.
for arg in args:
    if arg[0] != '@':
        # If pcitable not use, force must_reserve to 1
        #must_reserve = 0
        if pcitable:
            must_reserve = 0
        else:
            must_reserve = 1
    else:
        must_reserve = 1
        arg = arg[1:]
    orig_arg = arg
    if arg[0] == '/':  arg = arg[1:]
    if os.path.isdir(arg):
        os.path.walk(arg, scan_km_in_dir, must_reserve)
    elif os.path.isdir(os.path.join('lib/modules', kernelver, arg)):
        os.path.walk(os.path.join('lib/modules', kernelver, arg),
                     scan_km_in_dir, must_reserve)
    else:
        kmobj = kmd[os.path.splitext(orig_arg)[0]]
        kmod_map[kmobj] = must_reserve

# Accomplish the dependencies.
for kmobj in kmod_map.keys():
    for depobj in kmobj.fulldeps():
        if not kmod_map.has_key(depobj):
            kmod_map[depobj] = kmod_map[kmobj]
        elif kmod_map[kmobj] > kmod_map[depobj]:
            kmod_map[depobj] = kmod_map[kmobj]

def parse_line(line):
    # NOTICE: This function can't get the exactly string in the provided line.
    # Because it combine all spaces into one spaces in string, but this does not
    # affect its usage in parse 'pcitable'.
    s0 = string.split(line)
    result = []
    in_str = 0
    tmpstr = ''
    for s in s0:
        if in_str:
            tmpstr = tmpstr + ' ' + s
            if s[-1] == '"':
                result.append(tmpstr)
                tmpstr = ''
                in_str = 0
        else:
            if s[0] == '"' and s[-1] != '"':
                tmpstr = s
                in_str = 1
            else:
                result.append(s)
    return  result

# Scan the pcitable. Mark the found modules, generate cutdown pcitable.
if pcitable:
    pcitable_f = file(pcitable)
    l = pcitable_f.readline()
    pciitems = {}
    while l:
        if l[0] != '#' and string.strip(l) != '':  # Skip comment line and blank line.
            pciitem = parse_line(l)
            if 'Card:Intel' in pciitem[-1]: # Add intel video cards
                if cutdown_f:
                    cutdown_f.write(l)
            else:
                try:
                    kmobj = kmd[pciitem[-1][1:-1]]
                    if kmod_map.has_key(kmobj):
                        kmod_map[kmobj] = 1
                        pciitems.setdefault(pciitem[0], []).append(pciitem)
                        if cutdown_f:
                            cutdown_f.write(l)
                except KeyError:
                    pass
        l = pcitable_f.readline()
    # Close the cutdown pcitable if it is requested.
    if cutdown_f:
        cutdown_f.close()
    pcitable_f.close()

# Cut down pci.ids
if cutdown_pciids_f:
    pciids_f = file(pciids)
    l = pciids_f.readline()
    while l:
        # vendor  vendor_name
        #	device  device_name				<-- single tab
        #		subvendor subdevice  subsystem_name	<-- two tabs
        if l[0] != '#' and string.strip(l) != '' and l[0] != 'C':  # Skip comment line and blank line, device classes.
            do_output = False
            parts = l.split()
            if l[0] != '\t':
                vendor_id = '0x' + parts[0]
                device_id = subvendor_id = subdevice_id = ''
                if vendor_id in pciitems:
                    status = 0
                    do_output = True
                else:
                    status = -1
            elif l[0] == '\t' and l[1] != '\t' and status >= 0:
                device_id = '0x' + parts[0]
                subvendor_id = subdevice_id = ''
                if device_id in map(lambda i: i[1], pciitems[vendor_id]):
                    status = 1
                    do_output = True
                else:
                    status = 0
            elif l[0] == '\t' and l[1] == '\t' and status >= 1:
                subvendor_id = '0x' + parts[0]
                subdevice_id = '0x' + parts[1]
                for pciitem in pciitems[vendor_id]:
                    if len(pciitem) == 5 and pciitem[2] == subvendor_id and pciitem[3] == subdevice_id:
                        do_output = True
                        break
            if do_output:
                cutdown_pciids_f.write(l)
        l = pciids_f.readline()
    pciids_f.close()
    cutdown_pciids_f.close()

# Remove all useless modules.
result_list = filter(lambda kmobj: kmod_map[kmobj], kmod_map.keys())

# Accomplish module dependences again.
result = {}
for kmobj in result_list:
    result[kmobj] = 1
    for depobj in kmobj.fulldeps():
        result[depobj] = 1

# Show it.
print  string.join(map(lambda kmobj: str(kmobj)[1:], result.keys()), '\n')
